package top.hudk.wage;

import lombok.Data;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * 工资计算器
 *
 * @author HuDongkang
 * @date 2022/3/19 20:46
 */
public class WageCalculator {


    private static final BigDecimal TAX_RATE1 = new BigDecimal("0.03");
    private static final BigDecimal TAX_RATE2 = new BigDecimal("0.10");
    private static final BigDecimal TAX_RATE3 = new BigDecimal("0.20");
    private static final BigDecimal TAX_RATE4 = new BigDecimal("0.30");
    private static final BigDecimal TAX_RATE5 = new BigDecimal("0.35");
    private static final BigDecimal TAX_RATE6 = new BigDecimal("0.40");
    private static final BigDecimal TAX_RATE7 = new BigDecimal("0.45");

    private static final BigDecimal TAX_BASE_DEDUCTION = new BigDecimal(5000);
    private static final BigDecimal TAX_1_36000 = new BigDecimal(36000);
    private static final BigDecimal TAX_2_144000 = new BigDecimal(144000);
    private static final BigDecimal TAX_3_300000 = new BigDecimal(300000);
    private static final BigDecimal TAX_4_420000 = new BigDecimal(420000);
    private static final BigDecimal TAX_5_660000 = new BigDecimal(660000);
    private static final BigDecimal TAX_6_960000 = new BigDecimal(960000);

    private static final BigDecimal RATE_GONG_JI_JIN = new BigDecimal("0.12");
    private static final BigDecimal RATE_MEDICAL_CARE = new BigDecimal("0.0202521");
    private static final BigDecimal RATE_YANG_LAO = new BigDecimal("0.08");
    private static final BigDecimal RATE_SHI_YE = new BigDecimal("0.005");

    private static final BigDecimal SOCIAL_SECURITY_BASE = new BigDecimal("11900");

    private static final BigDecimal SPECIAL_DEDUCTION = new BigDecimal("2000");

    private static final BigDecimal[] WAGES = new BigDecimal[]{
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600"),
            new BigDecimal("30600")
    };

    public WageCalculator(){

    }

    private static final int MONTH_NUM = 12;


    public static void main(String[] args) {
        List<MonthWage> monthWageList = new ArrayList<>();
        MonthWage monthWage = new MonthWage(null, WAGES[0], SOCIAL_SECURITY_BASE, SPECIAL_DEDUCTION);
        monthWageList.add(monthWage);
        calculator(monthWage, monthWageList);
//        for(int wage = 10000; wage <= 1000000; wage += 10000){
//            BigDecimal tax = tax(new BigDecimal(wage));
//            System.out.println(wage + " : " + tax.intValue());
//        }
    }

    public static void calculator(MonthWage monthWage, List<MonthWage> monthWageList){
        if(monthWage == null || monthWage.month >= MONTH_NUM){
            return ;
        }
        MonthWage nextMonthWage = new MonthWage(monthWage, WAGES[monthWage.month], SOCIAL_SECURITY_BASE, SPECIAL_DEDUCTION);
        monthWageList.add(nextMonthWage);
        calculator(nextMonthWage, monthWageList);
    }


    /**
     * 计算个税金额
     * @param cumulativeWage 累计收入
     * @return 累计应纳税额
     */
    public static BigDecimal tax(BigDecimal cumulativeWage){
        BigDecimal cumulativeTaxWage = cumulativeWage.subtract(TAX_BASE_DEDUCTION.multiply(new BigDecimal(12)));
        BigDecimal tax = BigDecimal.ZERO;
        if(cumulativeTaxWage.compareTo(BigDecimal.ZERO) > 0 && cumulativeTaxWage.compareTo(TAX_1_36000) <= 0){
            tax = cumulativeTaxWage.multiply(TAX_RATE1).setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_1_36000) > 0 && cumulativeTaxWage.compareTo(TAX_2_144000) <= 0){
            tax = TAX_1_36000.multiply(TAX_RATE1)
                    .add(cumulativeTaxWage.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_2_144000) > 0 && cumulativeTaxWage.compareTo(TAX_3_300000) <= 0){
            tax =  TAX_1_36000.multiply(TAX_RATE1)
                    .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .add(cumulativeTaxWage.subtract(TAX_2_144000).multiply(TAX_RATE3))
                    .setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_3_300000) > 0 && cumulativeTaxWage.compareTo(TAX_4_420000) <= 0){
            tax =  TAX_1_36000.multiply(TAX_RATE1)
                    .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                    .add(cumulativeTaxWage.subtract(TAX_3_300000).multiply(TAX_RATE4))
                    .setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_4_420000) > 0 && cumulativeTaxWage.compareTo(TAX_5_660000) <= 0){
            tax =  TAX_1_36000.multiply(TAX_RATE1)
                    .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                    .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                    .add(cumulativeTaxWage.subtract(TAX_4_420000).multiply(TAX_RATE5))
                    .setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_5_660000) > 0 && cumulativeTaxWage.compareTo(TAX_6_960000) <= 0){
            tax =  TAX_1_36000.multiply(TAX_RATE1)
                    .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                    .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                    .add(TAX_5_660000.subtract(TAX_4_420000).multiply(TAX_RATE5))
                    .add(cumulativeTaxWage.subtract(TAX_5_660000).multiply(TAX_RATE6))
                    .setScale(2, RoundingMode.HALF_UP);
        }else if(cumulativeTaxWage.compareTo(TAX_6_960000) > 0){
            tax =  TAX_1_36000.multiply(TAX_RATE1)
                    .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                    .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                    .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                    .add(TAX_5_660000.subtract(TAX_4_420000).multiply(TAX_RATE5))
                    .add(TAX_6_960000.subtract(TAX_5_660000).multiply(TAX_RATE6))
                    .add(cumulativeTaxWage.subtract(TAX_6_960000).multiply(TAX_RATE7))
                    .setScale(2, RoundingMode.HALF_UP);
        }
        return tax;
    }


    /**
     * 月薪
     */
    @Data
    public static class MonthWage {

        /**
         * 月份
         */
        private int month;

        /**
         * 前一个月
         */
        private MonthWage preMonthMonthWage;

        private BigDecimal wage;
        private BigDecimal cumulativeWage;

        private BigDecimal socialSecurityBase;

        private BigDecimal grGongJiJin;
        private BigDecimal gsGongJiJin;
        private BigDecimal totalGongJiJin;

        private BigDecimal grMedicalCare;
        private BigDecimal gsMedicalCare;
        private BigDecimal totalMedicalCare;

        private BigDecimal grYangLao;
        private BigDecimal gsYangLao;
        private BigDecimal totalYangLao;

        private BigDecimal grShiYe;
        private BigDecimal gsShiYe;
        private BigDecimal totalShiYe;

        private BigDecimal socialSecurityDeduction;
        private BigDecimal cumulativeSocialSecurityDeduction;

        private BigDecimal specialDeduction;
        private BigDecimal cumulativeSpecialDeduction;

        private BigDecimal cumulativeTaxWage;

        private BigDecimal taxRate;
        private BigDecimal tax;
        private BigDecimal cumulativeTax;

        private BigDecimal realPayWages;
        private BigDecimal cumulativeRealPayWages;

        public MonthWage(MonthWage preMonthMonthWage, BigDecimal wage, BigDecimal socialSecurityBase, BigDecimal specialDeduction){
            //本月收入
            this.wage = wage;
            //累计收入
            if(preMonthMonthWage == null){
                this.month = 1;
                this.cumulativeWage = wage;
            }else {
                this.preMonthMonthWage = preMonthMonthWage;
                this.month = preMonthMonthWage.getMonth() + 1;
                this.cumulativeWage = preMonthMonthWage.getCumulativeWage().add(wage);
            }

            //社保基数
            this.socialSecurityBase = socialSecurityBase;

            //五险一金计算
            this.grGongJiJin = socialSecurityBase.multiply(RATE_GONG_JI_JIN).setScale(2, RoundingMode.HALF_UP);
            this.grMedicalCare = socialSecurityBase.multiply(RATE_MEDICAL_CARE).setScale(2, RoundingMode.HALF_UP);
            this.grYangLao = socialSecurityBase.multiply(RATE_YANG_LAO).setScale(2, RoundingMode.HALF_UP);
            this.grShiYe = socialSecurityBase.multiply(RATE_SHI_YE).setScale(2, RoundingMode.HALF_UP);

            //本月五险一金扣减
            this.socialSecurityDeduction = grGongJiJin.add(grMedicalCare).add(grYangLao).add(grShiYe);
            //累计扣减
            if(preMonthMonthWage == null){
                this.cumulativeSocialSecurityDeduction = this.socialSecurityDeduction;
            }else {
                this.cumulativeSocialSecurityDeduction = socialSecurityDeduction.add(preMonthMonthWage.cumulativeSocialSecurityDeduction);
            }

            //本月专项扣减
            this.specialDeduction = specialDeduction;
            //累计专项扣减
            if(preMonthMonthWage == null){
                this.cumulativeSpecialDeduction = this.specialDeduction;
            }else {
                this.cumulativeSpecialDeduction = this.specialDeduction.add(preMonthMonthWage.cumulativeSpecialDeduction);
            }

            //累计应纳税收入 = 累计收入 - 五险一金扣减 - 专项扣减
            this.cumulativeTaxWage = this.cumulativeWage
                    .subtract(this.cumulativeSocialSecurityDeduction)
                    .subtract(this.cumulativeSpecialDeduction)
                    .subtract(TAX_BASE_DEDUCTION);

            //累计应纳税额
            this.cumulativeTax = tax(this.cumulativeTaxWage);
            if(preMonthMonthWage == null){
                this.tax = this.cumulativeTax;
            }else {
                this.tax = this.cumulativeTax.subtract(preMonthMonthWage.cumulativeTax);
            }
            this.realPayWages = this.wage.subtract(socialSecurityDeduction).subtract(tax);
            if(preMonthMonthWage == null){
                this.cumulativeRealPayWages = this.realPayWages;
            }else {
                this.cumulativeRealPayWages = this.realPayWages.add(preMonthMonthWage.getCumulativeRealPayWages());
            }

            String s = toString();
            System.out.println(s);
        }

        /**
         * 计算个税金额
         * @param cumulativeTaxWage 累计收入
         * @return 累计应纳税额
         */
        public BigDecimal tax(BigDecimal cumulativeTaxWage){
            BigDecimal tax = BigDecimal.ZERO;
            if(cumulativeTaxWage.compareTo(BigDecimal.ZERO) > 0 && cumulativeTaxWage.compareTo(TAX_1_36000) <= 0){
                tax = cumulativeTaxWage.multiply(TAX_RATE1).setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE1;
            }else if(cumulativeTaxWage.compareTo(TAX_1_36000) > 0 && cumulativeTaxWage.compareTo(TAX_2_144000) <= 0){
                tax = TAX_1_36000.multiply(TAX_RATE1)
                        .add(cumulativeTaxWage.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE2;
            }else if(cumulativeTaxWage.compareTo(TAX_2_144000) > 0 && cumulativeTaxWage.compareTo(TAX_3_300000) <= 0){
                tax =  TAX_1_36000.multiply(TAX_RATE1)
                        .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .add(cumulativeTaxWage.subtract(TAX_2_144000).multiply(TAX_RATE3))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE3;
            }else if(cumulativeTaxWage.compareTo(TAX_3_300000) > 0 && cumulativeTaxWage.compareTo(TAX_4_420000) <= 0){
                tax =  TAX_1_36000.multiply(TAX_RATE1)
                        .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                        .add(cumulativeTaxWage.subtract(TAX_3_300000).multiply(TAX_RATE4))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE4;
            }else if(cumulativeTaxWage.compareTo(TAX_4_420000) > 0 && cumulativeTaxWage.compareTo(TAX_5_660000) <= 0){
                tax =  TAX_1_36000.multiply(TAX_RATE1)
                        .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                        .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                        .add(cumulativeTaxWage.subtract(TAX_4_420000).multiply(TAX_RATE5))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE5;
            }else if(cumulativeTaxWage.compareTo(TAX_5_660000) > 0 && cumulativeTaxWage.compareTo(TAX_6_960000) <= 0){
                tax =  TAX_1_36000.multiply(TAX_RATE1)
                        .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                        .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                        .add(TAX_5_660000.subtract(TAX_4_420000).multiply(TAX_RATE5))
                        .add(cumulativeTaxWage.subtract(TAX_5_660000).multiply(TAX_RATE6))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE6;
            }else if(cumulativeTaxWage.compareTo(TAX_6_960000) > 0){
                tax =  TAX_1_36000.multiply(TAX_RATE1)
                        .add(TAX_2_144000.subtract(TAX_1_36000).multiply(TAX_RATE2))
                        .add(TAX_3_300000.subtract(TAX_2_144000).multiply(TAX_RATE3))
                        .add(TAX_4_420000.subtract(TAX_3_300000).multiply(TAX_RATE4))
                        .add(TAX_5_660000.subtract(TAX_4_420000).multiply(TAX_RATE5))
                        .add(TAX_6_960000.subtract(TAX_5_660000).multiply(TAX_RATE6))
                        .add(cumulativeTaxWage.subtract(TAX_6_960000).multiply(TAX_RATE7))
                        .setScale(2, RoundingMode.HALF_UP);
                this.taxRate = TAX_RATE7;
            }
            return tax;
        }

        @Override
        public String toString(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("##############\n");
            stringBuilder.append("分月：" + this.month + "\n");
            stringBuilder.append("本月收入：" + this.wage + "\n");
            stringBuilder.append("年度累计收入：" + this.cumulativeWage + "\n");
            stringBuilder.append("社保基数：" + this.socialSecurityBase + "\n");
            stringBuilder.append("  本月公积金：" + this.grGongJiJin + "\n");
            stringBuilder.append("  本月医疗保险：" + this.grMedicalCare + "\n");
            stringBuilder.append("  本月养老保险：" + this.grYangLao + "\n");
            stringBuilder.append("  本月失业保险：" + this.grShiYe + "\n");
            stringBuilder.append("本月五险一金扣减：" + this.socialSecurityDeduction + "\n");
            stringBuilder.append("累计五险一金扣减：" + this.cumulativeSocialSecurityDeduction + "\n");
            stringBuilder.append("本月专项扣减：" + this.specialDeduction + "\n");
            stringBuilder.append("累计专项扣减：" + this.cumulativeSpecialDeduction + "\n");
            stringBuilder.append("累计应纳税收入：" + this.cumulativeTaxWage + "\n");
            stringBuilder.append("适用税率：" + this.taxRate + "\n");
            stringBuilder.append("累计纳税：" + this.cumulativeTax + "\n");
            stringBuilder.append("本月应纳税：" + this.tax + "\n");
            stringBuilder.append("本月实发：" + this.realPayWages + "\n");
            stringBuilder.append("累计实发：" + this.cumulativeRealPayWages + "\n");
            return stringBuilder.toString();
        }

    }



}
